#include <pio.h>
#include <std.h>
#define MONO 1
#include <error.h>
#include <dint.h>
#include <memory.h>
#include <irq.h>
#include <8259.h>

/**************************Kernel Function File*****************************irq.h*/

/*Direct dependency driver: i386/8259.c*/

extern kdrv i8259;

struct irq_handler	{
	void (*handler)();
	kdrv *driver;
	u32 count;
	struct irq_handler *next;
};

static struct irq_handler *head_irq,*temp;

static unsigned long chain_base[16];
static unsigned int irq_flags[16];
 

int kernel_lock()
{
	return (int)i8259.write_device(WRITE_LLOCK,0,0,0);
}

int kernel_unlock()
{
	return (int)i8259.write_device(WRITE_LLOCKFREE,0,0,0);
}

static struct mcount_struct	{
	char valid;
	u32 count;
}mcount[16];

int got_irq(int irqno)
{
	u32 ret;
	temp=(struct irq_handler *)chain_base[irqno];
	if(mcount[irqno].valid==NULL)	{
		mcount[irqno].valid=1;
		mcount[irqno].count=temp->count;
	} else	{
		ret=temp->count - mcount[irqno].count;
		mcount[irqno].count=temp->count;
		return ret;
	}
	return NULL;
}

void init_mcount()
{
	int i;
	for(i=0;i<16;i++)	{
		mcount[i].valid=NULL;
		mcount[i].count=NULL;
	}
}

void init_irq_chain()
{
	int i;
	for(i=0;i<16;i++)	{
		create_head(struct irq_handler,head_irq);
		chain_base[i]=(unsigned long)head_irq;
		irq_flags[i]=NULL;
	}
	init_mcount();
}

static int chain_handler(kdrv *driver,int irq,void *handler)
{
	struct irq_handler *new;
	kernel_lock();
	if(irq_flags[irq]==MONO)
		return -1;
	head_irq=(struct irq_handler *)chain_base[irq];
	add_node_end(struct irq_handler,head_irq,new);
	new->handler=handler;
	new->driver=driver;
	kernel_unlock();
	return NULL;
}

static int delete_handler(int irq,void *handler)
{
	struct irq_handler *head_irq,*t;
	kernel_lock();
	head_irq=(struct irq_handler *)chain_base[irq];
	for(t=head_irq;(t->handler)!=handler;t=t->next);
	delete_node(struct irq_handler,head_irq,t);
	kernel_unlock();
	return NULL;
}

int request_irq(kdrv *driver,int irq,void *handler)
{
	if(!handler || irq>15 || irq<0)	{
		printk("\nInvalid irq request!");
		return NULL;
	}
	chain_handler(driver,irq,handler);
	return NULL;
}

int release_irq(int irq,void *handler)
{
	if(!handler || irq>15 || irq<0)	{
		printk("\nInvalid irq release!");
		return NULL;
	}
	delete_handler(irq,handler);
	return NULL;
}

/*This function allows a device driver to have only itself hooked to the irq.*/
int request_irq_mono(kdrv *driver,int irq,void *handler)
{
	int i;
	i=chain_handler(driver,irq,handler);
	if(i==-1)
		error("IRQ mono:grant failed");
	return i;
}

u32 get_irq_count(int irqno)
{
	struct irq_handler *t;
	t=(struct irq_handler *)chain_base[irqno];
	return t->count;
}

u32 *byref_get_irq_count(int irqno)
{
	struct irq_handler *t;
	t=(struct irq_handler *)chain_base[irqno];
	return &t->count;
}

void init_irq() 
{
	printk("\nInitiating IRQ");
	i8259.init();
	init_irq_chain();
}

void _uirq0()
{
	head_irq=(struct irq_handler *)chain_base[0];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,0,0,0);
}

void _uirq1()
{
	head_irq=(struct irq_handler *)chain_base[1];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,1,0,0);
}

void _uirq2()
{
	head_irq=(struct irq_handler *)chain_base[2];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,2,0,0);
}

void _uirq3()
{
	head_irq=(struct irq_handler *)chain_base[3];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,3,0,0);
}

void _uirq4()
{
	head_irq=(struct irq_handler *)chain_base[4];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,4,0,0);
}

void _uirq5()
{
	head_irq=(struct irq_handler *)chain_base[5];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,5,0,0);
}

void _uirq6()
{
	head_irq=(struct irq_handler *)chain_base[6];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,6,0,0);
}

void _uirq7()
{
	head_irq=(struct irq_handler *)chain_base[7];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,7,0,0);
}

void _uirq8()
{
 	head_irq=(struct irq_handler *)chain_base[8];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,8,0,0);
}

void _uirq9()
{
	head_irq=(struct irq_handler *)chain_base[9];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,9,0,0);
}

void _uirq10()
{
	head_irq=(struct irq_handler *)chain_base[10];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,10,0,0);
}

void _uirq11()
{
	head_irq=(struct irq_handler *)chain_base[11];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,11,0,0);
}

void _uirq12()
{
	head_irq=(struct irq_handler *)chain_base[12];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,12,0,0);
}

void _uirq13()
{
	head_irq=(struct irq_handler *)chain_base[13];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,13,0,0);
}

void _uirq14()
{
	head_irq=(struct irq_handler *)chain_base[14];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,14,0,0);
}

void _uirq15()
{
	head_irq=(struct irq_handler *)chain_base[15];
	head_irq->count++;
	for(temp=head_irq->next;temp!=NULL;temp=temp->next)	{
		(temp->handler)();
	}
	i8259.write_device(WRITE_IRQ_ENDED,15,0,0);
}
